package io.xxx.omni.osc.domain

import com.alibaba.fastjson.JSON
import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import io.xxx.omni.osc.common.dateTimeFormatPattern
import io.xxx.omni.osc.common.format
import io.xxx.omni.osc.common.toJSONString
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Param
import org.hibernate.Hibernate
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDateTime
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id

enum class DocumentType(val description: String) {

    /**
     * 订单
     */
    TRADE("Trade"),

    /**
     * 退单
     */
    REFUND("Refund"),

    /**
     * 既可能是订单也可能是退单或者其他数据类型
     */
    NONE("");
}

/**
 * 报文
 */
data class Document(
    /**
     * 标记数据来源
     */
    var porterId: String?,
    /**
     * 平台编码
     */
    var platformId: String?,
    /**
     * 店铺编码
     */
    var storeId: String?,
    var sn: String?,
    val rsn: String?,
    val status: String?,
    val modified: LocalDateTime?,
    val data: String?,
    val state: Int?,
    var pollCreated: LocalDateTime?,
    var pollModified: LocalDateTime?,
) {
    @Suppress("unused")
    constructor() : this(null, null, null, null, null, null, null, null, null, null, null)

    constructor(sn: String, status: String?, modified: LocalDateTime, data: Any)
            : this(sn, null, status, modified, data)

    constructor(sn: String, rsn: String?, status: String?, modified: LocalDateTime?, data: Any)
            : this(
        null,
        null,
        null,
        sn,
        rsn,
        status,
        modified,
        data.toJSONString()!!.replace("\\u0000", ""),
        0,
        null,
        null
    )

    constructor(sn: String, rsn: String, status: String?, data: Any)
            : this(sn, rsn, status, null, data)

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
        other as Document
        return storeId == other.storeId && sn == other.sn
    }

    override fun hashCode(): Int = 1422296640

    override fun toString(): String {
        return JSON.toJSONStringWithDateFormat(this, "yyyy-MM-dd HH:mm:ss")
    }

    operator fun compareTo(other: Document): Int {
        return modified?.compareTo(other.modified) ?: 1
    }

    fun toMessage(): String {
        val map = mapOf(
            "storeId" to storeId,
            "sn" to sn,
            "rsn" to rsn,
            "modified" to modified?.format(),
        )
        return map.toJSONString()!!
    }
}

@Mapper
interface DocumentMapper : BaseMapper<Document> {

    fun insertAll(@Param("list") documents: List<Document>)

    fun updateAll(@Param("list") documents: List<Document>)

    fun upsertAll(@Param("list") documents: List<Document>)
}

data class RetryDocument(
    val platformId: String,
    val storeId: String,
    val sn: String,
    val count: Int,
    val created: LocalDateTime,
    val modified: LocalDateTime,
) {
    constructor(platformId: String, storeId: String, sn: String, created: LocalDateTime)
            : this(platformId, storeId, sn, 1, created, created)
}

@Mapper
interface RetryDocumentMapper : BaseMapper<RetryDocument>

@Entity
data class CommittedOffset(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long?,
    val topic: String,
    val partition: Int,
    val value: Long,
    var created: LocalDateTime?,
    var modified: LocalDateTime?,
) {
    @Suppress("unused")
    constructor(topic: String, partition: Int, value: Long)
            : this(null, topic, partition, value, null, null) {
        val now = LocalDateTime.now()
        this.created = now
        this.modified = now
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
        other as CommittedOffset

        return id != null && id == other.id
    }

    override fun hashCode(): Int = 0

    override fun toString(): String {
        return JSON.toJSONStringWithDateFormat(this, dateTimeFormatPattern)
    }
}

interface CommittedOffsetRepository : JpaRepository<CommittedOffset, Long>

@Mapper
interface CommittedOffsetMapper : BaseMapper<CommittedOffset> {

    fun upsert(@Param("offset") offset: CommittedOffset)
}